/********************************
 * ETK - Easy ToolKit
 * Copyright (C) 2007-2008 Ekinoks (scv.ekinoks@gmail.com)
 * All rights reserved.
 *
 *    This file is part of ETK.
 *
 *    Foobar is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    Foobar is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with Foobar; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 ***********************************************************************************/

#include "LibGtk.h"

#include <gtk/gtk.h>
#include <map>

#include "../../define.h"
#include "../../CallBack/LIST_EVENT.H"
#include "../../Widget/Widget.h"
#include "../../CallBack/Call.h"
#include "../../StateModel/StateModel.h"


namespace
{
    /// Stoquer le nom des evenements
    std::map<int, const char*> map_event_name;

    /// Stoquer le nombre d'argument de la fonction callback
    std::map<int, int> map_event_arg;
}

namespace etk
{
    void LibGtk::init(int argc, char *argv[]) const
    {
        gtk_init(&argc, &argv);


        // TODO : faire tout ca a la compilation !!!!
        map_event_name[EVENT_DELETE] = "delete_event";
        map_event_arg[EVENT_DELETE] = 3;

        map_event_name[EVENT_DESTROY] = "destroy";
        map_event_arg[EVENT_DESTROY] = 2;

        map_event_name[EVENT_BUTTON_PRESS] = "button_press_event";
        map_event_arg[EVENT_BUTTON_PRESS] = 3;

        map_event_name[EVENT_BUTTON_RELEASE] = "button_release_event";
        map_event_arg[EVENT_BUTTON_RELEASE] = 3;

        map_event_name[EVENT_SCROLL] = "scroll_event";
        map_event_arg[EVENT_SCROLL] = 3;

        map_event_name[EVENT_MOTION_NOTIFY] = "motion_notify_event";
        map_event_arg[EVENT_MOTION_NOTIFY] = 3;


        map_event_name[EVENT_KEY_PRESS] = "key_press_event";
        map_event_arg[EVENT_KEY_PRESS] = 3;

        map_event_name[EVENT_KEY_RELEASE] = "key_release_event";
        map_event_arg[EVENT_KEY_RELEASE] = 3;

        map_event_name[EVENT_FOCUS_IN] = "focus_in_event";
        map_event_arg[EVENT_FOCUS_IN] = 3;

        map_event_name[EVENT_FOCUS_OUT] = "focus_out_event";
        map_event_arg[EVENT_FOCUS_OUT] = 3;



        map_event_name[EVENT_CLICKED] = "clicked";
        map_event_arg[EVENT_CLICKED] = 2;

        /// TODO : rajouter tout les events

    }

    void LibGtk::widget_show(const RWidget *rWidget) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET( rWidget ) );
        gtk_widget_show(cast_widget);
    }

    void LibGtk::widget_hide(const RWidget *rWidget) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET( rWidget ) );
        gtk_widget_hide(cast_widget);
    }

    void LibGtk::add_essential_event(const RWidget *rWidget) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET( rWidget ) );

        gtk_widget_add_events/*gtk_widget_set_events*/(cast_widget, GDK_ALL_EVENTS_MASK/*GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_FOCUS_CHANGE_MASK*/);
    }

    long LibGtk::signal_connect(const RWidget *rWidget, const int event, CallBack * callBack) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET( rWidget ) );

        if(event == EVENT_MOTION_NOTIFY)
            gtk_widget_add_events (cast_widget, GDK_POINTER_MOTION_MASK);

        if(map_event_arg[event] == 3)
            return g_signal_connect (G_OBJECT ( getPWIDGET(rWidget) ), map_event_name[event], G_CALLBACK (Call::call3), callBack);
        return g_signal_connect (G_OBJECT ( getPWIDGET(rWidget) ), map_event_name[event], G_CALLBACK (Call::call2), callBack);
    }

    long LibGtk::signal_connect(const RWidget *rWidget, const int event, int(*f)(PWIDGET widget, int event, void* arg), void* arg) const
    {
        return g_signal_connect (G_OBJECT ( getPWIDGET(rWidget) ), map_event_name[event], G_CALLBACK(f), arg);
    }

    long LibGtk::signal_connect_after(const RWidget *rWidget, const int event, CallBack * callBack) const
    {
        if(map_event_arg[event] == 3)
            return g_signal_connect_after (G_OBJECT ( getPWIDGET(rWidget) ), map_event_name[event], G_CALLBACK (Call::call3), callBack);
        return g_signal_connect_after (G_OBJECT ( getPWIDGET(rWidget) ), map_event_name[event], G_CALLBACK (Call::call2), callBack);
    }

    long LibGtk::signal_connect_after(const RWidget *rWidget, const int event, int(*f)(PWIDGET widget, int event, void* arg), void* arg) const
    {
        return g_signal_connect_after(G_OBJECT ( getPWIDGET(rWidget) ), map_event_name[event], G_CALLBACK(f), arg);
    }

    void LibGtk::signal_disconnect(const RWidget* rWidget, const long id_signal) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET(rWidget) );
        g_signal_handler_disconnect(cast_widget, id_signal);
    }

    bool LibGtk::signal_is_connected(const RWidget *rWidget, const long id_signal) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET(rWidget) );
        return g_signal_handler_is_connected(cast_widget, id_signal);
    }

    void LibGtk::signal_block(const RWidget* rWidget, const long id_signal) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET(rWidget) );

         g_signal_handler_block( cast_widget, id_signal);
    }

    void LibGtk::signal_unblock(const RWidget* rWidget, const long id_signal) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET(rWidget) );

        g_signal_handler_unblock( cast_widget, id_signal);
    }

   RWidget* LibGtk::window_new() const
   {
        RWidget *result = new RWidget(gtk_window_new(GTK_WINDOW_TOPLEVEL));

        widget_show(result);

        add_essential_event(result);    /// TODO : faire un gtk_widget_add_events de l'event que si on en a besoin

       return result;  // TODO : donner la possiblité de faire autre chose que des GTK_WINDOW_TOPLEVEL
   }

   RWidget* LibGtk::box_new(const position_e position, const bool homogeneous, const int spacing) const
   {
        GtkWidget* tmp;
        if(position == VERTICAL)
            tmp = gtk_vbox_new(homogeneous, spacing);
        else /*if(vertical == HORIZONTAL)*/
            tmp = gtk_hbox_new(homogeneous, spacing);

        RWidget *result = new RWidget(tmp);

        widget_show(result);

        add_essential_event(result);    /// TODO : faire un gtk_widget_add_events de l'event que si on en a besoin

        return result;
   }

    RWidget* LibGtk::button_new_with_label(const char* label)
    {
        RWidget *result = new RWidget(gtk_button_new_with_label(label) );

        widget_show(result);

        add_essential_event(result);    /// TODO : faire un gtk_widget_add_events de l'event que si on en a besoin

        return result;
    }

    void LibGtk::window_set_title(const RWidget *rWidget, const /*char**/ std::string titre) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET(rWidget) );

        gtk_window_set_title( GTK_WINDOW( cast_widget ), titre.c_str() );
    }

    void LibGtk::window_set_size(const RWidget *rWidget, const int size_x, const int size_y) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET(rWidget) );

        gtk_widget_set_usize( cast_widget, size_x, size_y );
    }

    void LibGtk::window_set_position(const RWidget *rWidget, const int x, const int y) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET(rWidget) );

        gtk_widget_set_uposition (cast_widget, x, y);
    }

    void LibGtk::go() const
    {
        gtk_main();
    }

    void LibGtk::widget_destroy( const RWidget *rWidget ) const
    {
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET(rWidget) );

        gtk_widget_destroy(cast_widget);
    }

    void LibGtk::container_add(const RWidget *container, const RWidget *rWidget) const
    {
        GtkWidget* cast_container = static_cast<GtkWidget*>( getPWIDGET(container) );
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET(rWidget) );

        gtk_container_add (GTK_CONTAINER (cast_container), cast_widget);
    }

    void LibGtk::container_remove(const RWidget *container, const RWidget *rWidget) const
    {
        GtkWidget* cast_container = static_cast<GtkWidget*>( getPWIDGET(container) );
        GtkWidget* cast_widget = static_cast<GtkWidget*>( getPWIDGET(rWidget) );

        gtk_container_remove (GTK_CONTAINER (cast_container), cast_widget);
    }

    // //////////////////////////////////////////////////////////////////////////////////////////////////
    // /////////////////////////////// EVENT ///////////////////////////////////////////////////////////
    // ////////////////////////////////////////////////////////////////////////////////////////////////

    void LibGtk::updateState(State &state, int long lstate) const
    {
        lstate += (~lstate) << 19;

        setState(state, lstate);
    }

    void LibGtk::updateEvent(EventButton &e, void* realEvent) const
    {
        GdkEventButton* event = static_cast<GdkEventButton*>( realEvent );

        assert( (event->type == GDK_BUTTON_PRESS)   ||
                (event->type == GDK_2BUTTON_PRESS)  ||
                (event->type == GDK_3BUTTON_PRESS)  ||
                (event->type == GDK_BUTTON_RELEASE) );

        EventButtonImpl &impl = getImpl<EventButtonImpl, EventButton>(e);

        impl.x_root = event->x_root;
        impl.y_root = event->y_root;

        impl.x = event->x;
        impl.y = event->y;

        updateState( impl.state, event->state );

        setButton(impl.shutterButton, 1 << (event->button - 1));    // pour exemplication se reporter a ShutterButton.h
        //impl.shutterButton.button_ = 1 << (event->button - 1);

        switch( event->type )// pour exemplication se reporter a ShutterButton.h
        {
            case GDK_BUTTON_PRESS :
                setEventType(impl.shutterButton, 1<<0);
            break;

            case GDK_2BUTTON_PRESS :
                setEventType(impl.shutterButton, 1<<1);
            break;

            case GDK_3BUTTON_PRESS :
                setEventType(impl.shutterButton, 1<<2);
            break;

            case GDK_BUTTON_RELEASE :
                setEventType(impl.shutterButton, 1<<3);
            break;

            default :
                assert(!"Error type event");
        }

    }

    void LibGtk::updateEvent(EventScroll &e, void* realEvent) const
    {
        GdkEventScroll* event = static_cast<GdkEventScroll*>( realEvent );

        assert( event->type == GDK_SCROLL );

        EventScrollImpl &impl = getImpl<EventScrollImpl, EventScroll>(e);

        impl.x_root = event->x_root;
        impl.y_root = event->y_root;

        impl.x = event->x;
        impl.y = event->y;

        updateState( impl.state, event->state );

        switch( event->direction )  // pour exemplication se reporter a ShutterScrollModel.h
        {
            case GDK_SCROLL_UP :
                setDirection( impl.shutterScroll, 1<<0);
            break;

            case GDK_SCROLL_DOWN :
                setDirection( impl.shutterScroll, 1<<1);
            break;

            case GDK_SCROLL_LEFT :
                setDirection( impl.shutterScroll, 1<<2);
            break;

            case GDK_SCROLL_RIGHT :
                setDirection( impl.shutterScroll, 1<<3);
            break;

            default :
                assert(!"Error type event");
        }
    }

    void LibGtk::updateEvent(EventKey &e, void* realEvent) const
    {
        GdkEventKey* event = static_cast<GdkEventKey*>( realEvent );

        EventKeyImpl &impl = getImpl<EventKeyImpl, EventKey>(e);

        updateState( impl.state, event->state );

        setKeyCode(impl.shutterKey, event->hardware_keycode);
        setCharacter(impl.shutterKey, event->string);
    }

    void LibGtk::updateEvent(EventMotion &e, void* realEvent) const
    {
        GdkEventMotion* event = static_cast<GdkEventMotion*>( realEvent );

        EventMotionImpl &impl = getImpl<EventMotionImpl, EventMotion>(e);

        updateState( impl.state, event->state );


    }

    // FIN EVENT //////////////////////////////////////////////////////////////////////////////////////
}

